/**
 * GroupTaskManagerImpl.java
 * com.sitech.dmcs.core.impl
 * Function： TODO 
 *
 *   ver  author   date    
 * ──────────────────────────────────
 *   	 geutopia  Mar 3, 2011        
 *
 * Copyright (c) 2011, TNT All Rights Reserved.
*/

package com.sitech.timing;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.sitech.timing.TimingConst;
import com.sitech.timing.domain.GroupTaskInfo;
import com.sitech.timing.domain.GroupTaskInstance;
import com.sitech.timing.param.ParamService;
import com.sitech.timing.dao.TaskDao;
import com.sitech.timing.domain.TaskInfo;
import com.sitech.timing.domain.TaskInstance;
import com.sitech.timing.domain.TaskType;
import com.sitech.service.SerialService;
import com.sitech.timing.GroupTaskContext;
import com.sitech.timing.GroupTaskManager;
import com.sitech.timing.RunTask;
import com.sitech.timing.Task;
import com.sitech.timing.TaskTypeFactory;

/**
 * ClassName:GroupTaskManagerImpl
 * Function: TODO ADD FUNCTION
 * Reason:	 TODO ADD REASON
 *
 * @author   geutopia
 * @version  
 * @since    Ver 1.1
 * @Date	 2011	Mar 3, 2011		3:54:55 PM
 *
 * @see 	 
 */

public class GroupTaskManagerImpl implements GroupTaskManager
{
private static final Log log = LogFactory.getLog(GroupTaskManagerImpl.class);
	
	private TaskTypeFactory taskTypeFactory;
	private TaskDao taskDao;
	private SerialService serialService;
	private ParamService paramService;
	
	public void init(GroupTaskInfo groupTaskInfo)
	{	
		init(groupTaskInfo, null);
	}
	
	/**
	 * (non-Javadoc)
	 * @see com.sitech.timing.GroupTaskManager#init(com.sitech.timing.domain.GroupTaskInfo, java.util.Map)
	 */
	public void init(GroupTaskInfo groupTaskInfo, Map<String,Object> paramMap)
	{
		log.debug("run groupTask: GroupTaskId = " + groupTaskInfo.getGroupTaskId() + "and GroupTaskName = " +groupTaskInfo.getGroupTaskName() );
		
		if (groupTaskInfo != null)
		{
			GroupTaskInstance groupTaskInstance = createGroupTaskInstance(groupTaskInfo,null);
			GroupTaskContext.addRunTaskGroup(groupTaskInstance);
			List<TaskInfo> taskInfoList = getFirstTaskInfo(groupTaskInfo, -1);
			List<GroupTaskInfo> subGroupTaskInfoList = getFirstSubGroupTaskInfo(groupTaskInfo, -1);
			run(taskInfoList, subGroupTaskInfoList,groupTaskInstance, paramMap);
		}		
	}
	
	private void init(GroupTaskInfo groupTaskInfo, GroupTaskInstance superGroupTaskInstance, Map<String,Object> paramMap)
	{
		log.debug("run subGroupTask: GroupTaskId = " + groupTaskInfo.getGroupTaskId() + " SuperGroupTaskId = "
				+ superGroupTaskInstance.getGroupTaskId() + " and GroupTaskName = " + groupTaskInfo.getGroupTaskName());

		if (groupTaskInfo != null)
		{
			GroupTaskInstance groupTaskInstance = createGroupTaskInstance(groupTaskInfo,superGroupTaskInstance);
			GroupTaskContext.addRunTaskGroup(groupTaskInstance);
			List<TaskInfo> taskInfoList = getFirstTaskInfo(groupTaskInfo, -1);
			List<GroupTaskInfo> subGroupTaskInfoList = getFirstSubGroupTaskInfo(groupTaskInfo, -1);
			run(taskInfoList, subGroupTaskInfoList,groupTaskInstance,paramMap);
		}
	}
	
	private synchronized void run(List<TaskInfo> taskInfoList, List<GroupTaskInfo> subGroupTaskInfoList,GroupTaskInstance groupTaskInstance, Map<String,Object> paramMap)
	{
		if(taskInfoList != null && taskInfoList.size() >0 && subGroupTaskInfoList != null && subGroupTaskInfoList.size() >0)
		{
			if(taskInfoList.get(0).getOrderNo() < subGroupTaskInfoList.get(0).getOrderNo())
			{
				runTask(taskInfoList,groupTaskInstance,paramMap);
			}
			else if (taskInfoList.get(0).getOrderNo() == subGroupTaskInfoList.get(0).getOrderNo())
			{
				runTask(taskInfoList,groupTaskInstance,paramMap);
				runSubGroupTask(subGroupTaskInfoList,groupTaskInstance,paramMap);
			}
			else
			{
				runSubGroupTask(subGroupTaskInfoList,groupTaskInstance,paramMap);
			}
		}
		else if(taskInfoList != null && taskInfoList.size() > 0)
		{
			runTask(taskInfoList,groupTaskInstance,paramMap);
		}
		else if(subGroupTaskInfoList != null && subGroupTaskInfoList.size() >0 )
		{
			runSubGroupTask(subGroupTaskInfoList,groupTaskInstance,paramMap);
		}
		else
		{
			log.debug("GroupTask End. GroupTaskId =  " + groupTaskInstance.getGroupTaskId() + " GroupTaskInstanceId = " +  groupTaskInstance.getGroupTaskInstanceId());
			groupTaskInstance.setStatus(TimingConst.GROUPTASK_STATUS_COMPLETE);
			taskDao.updateGroupTaskInstance(groupTaskInstance);
			GroupTaskContext.groupTaskEnd(groupTaskInstance.getGroupTaskInstanceId());
			if(groupTaskInstance.getSuperGroupTaskInstanceId() != null)
			{
				generateNext(groupTaskInstance);
			}
		}
	}
	
	private void runSubGroupTask(List<GroupTaskInfo> subGroupTaskInfoList,GroupTaskInstance superGroupTaskInstance, Map<String,Object> paramMap)
	{
		for(GroupTaskInfo subGroupTaskInfo :subGroupTaskInfoList)
		{
			try
			{
				this.init(subGroupTaskInfo,superGroupTaskInstance,paramMap);
			}
			catch (Throwable throwable) 
			{
				log.error("run subGroupTask: GroupTaskId = " + subGroupTaskInfo.getGroupTaskId() + " SuperGroupTaskId = "
						+ superGroupTaskInstance.getGroupTaskId() + " and GroupTaskName = " + subGroupTaskInfo.getGroupTaskName(), throwable);
			}
		}
	}
	
	private void runTask(List<TaskInfo> taskInfoList,GroupTaskInstance groupTaskInstance, Map<String,Object> inParamMap)
	{
		for (TaskInfo taskInfo : taskInfoList)
		{
			TaskType taskType = taskInfo.getTaskType();
			Task task = taskTypeFactory.getTaskByTaskTaskType(taskType);
			TaskInstance taskInstance = createTaskInstance(taskInfo,groupTaskInstance);

			Map<String, Object> paramMap = paramService.produceParamMap(taskInstance);
			if(paramMap != null&& inParamMap!=null)
			{
				paramMap.putAll(inParamMap);
			}
			else if(paramMap == null && inParamMap != null)
			{
				paramMap  = inParamMap;
			}
				
			runTask(taskType, task, taskInstance, paramMap);
		}
	}
	
	private void generateNext(GroupTaskInstance groupTaskInstance)
	{
		if(check(groupTaskInstance))
		{	
			String superGroupTaskInstanceId = groupTaskInstance.getSuperGroupTaskInstanceId();
			GroupTaskInstance superGroupTaskInstance = GroupTaskContext.getRunTaskGroup(superGroupTaskInstanceId);
			GroupTaskInfo superGroupTaskInfo = GroupTaskContext.getGroupTaskInfo(superGroupTaskInstance.getGroupTaskId());
			GroupTaskInfo groupTaskInfo = GroupTaskContext.getGroupTaskInfo(groupTaskInstance.getGroupTaskId());

			runNext(groupTaskInstance.getSuperGroupTaskInstanceId(), superGroupTaskInfo.getGroupTaskId(), groupTaskInfo.getOrderNo());
		}
	}
	
	public void generateTask(TaskInstance taskInstance)
	{
		if(check(taskInstance))
		{
			TaskInfo taskInfo = GroupTaskContext.getTaskInfo(taskInstance.getGroupTaskId(), taskInstance.getTaskId());
			runNext(taskInstance.getGroupTaskInstanceId(), taskInstance.getGroupTaskId(), taskInfo.getOrderNo());
		}
	}
	
	private  void runNext(String groupTaskInstanceId, String groupTaskId, int orderNo)
	{
		GroupTaskInfo groupTaskInfo = GroupTaskContext.getGroupTaskInfo(groupTaskId);
		
		List<GroupTaskInfo> subGroupTaskInfoList = getFirstSubGroupTaskInfo(groupTaskInfo, orderNo);
		List<TaskInfo> taskInfoList = getFirstTaskInfo(groupTaskInfo, orderNo);
		
		GroupTaskInstance groupTaskInstance = GroupTaskContext.getRunTaskGroup(groupTaskInstanceId);
		run(taskInfoList, subGroupTaskInfoList,groupTaskInstance,null);
	}
	
	private boolean check(GroupTaskInstance groupTaskInstance)
	{
		String superGroupTaskInstanceId = groupTaskInstance.getSuperGroupTaskInstanceId();
		GroupTaskInfo groupTaskInfo = GroupTaskContext.getGroupTaskInfo(groupTaskInstance.getGroupTaskId());
		GroupTaskInstance superGroupTaskInstance = GroupTaskContext.getRunTaskGroup(superGroupTaskInstanceId);
		superGroupTaskInstance.getGroupTaskId();
		GroupTaskInfo superGroupTaskInfo = GroupTaskContext.getGroupTaskInfo(superGroupTaskInstance.getGroupTaskId());
		List<TaskInfo> sameLevelTaskList = getSameOrderTask(superGroupTaskInfo, groupTaskInfo.getOrderNo());
		List<GroupTaskInfo> sameLevelGroupTaskList = getSameSubGroupTask(superGroupTaskInfo, groupTaskInfo.getOrderNo());
		
		
		if(sameLevelTaskList != null && sameLevelTaskList.size() > 0)
		{
			for(TaskInfo sameLeveltaskInfo : sameLevelTaskList)
			{
				if(sameLeveltaskInfo != null && existTaskInstance(superGroupTaskInstanceId,sameLeveltaskInfo.getTaskInfoId()))
				{
					return false;
				}
			}
		}
		
		if(sameLevelGroupTaskList != null && sameLevelGroupTaskList.size() > 0)
		{
			for(GroupTaskInfo subGrouptask : sameLevelGroupTaskList)
			{
				if(subGrouptask != null && !subGrouptask.getGroupTaskId().equals(groupTaskInfo.getGroupTaskId()) && existGroupTaskInstance(superGroupTaskInstanceId, subGrouptask.getGroupTaskId()))
				{
					return false;
				}
			}
		}
		return true;


	}
	
	
	private boolean check(TaskInstance taskInstance)
	{
		String groupTaskInstanceId = taskInstance.getGroupTaskInstanceId();
		GroupTaskInfo groupTaskInfo = GroupTaskContext.getGroupTaskInfo(taskInstance.getGroupTaskId());
		TaskInfo taskInfo = GroupTaskContext.getTaskInfo(taskInstance.getGroupTaskId(), taskInstance.getTaskId());
		List<TaskInfo> sameLevelTaskList = getSameOrderTask(groupTaskInfo, taskInfo.getOrderNo());
		List<GroupTaskInfo> sameLevelGroupTaskList = getSameSubGroupTask(groupTaskInfo, taskInfo.getOrderNo());
		if(sameLevelTaskList != null && sameLevelTaskList.size() > 0)
		{
			for(TaskInfo sameLeveltaskInfo : sameLevelTaskList)
			{
				if(sameLeveltaskInfo != null && !sameLeveltaskInfo.getTaskInfoId().equals(taskInstance.getTaskId()) && existTaskInstance(groupTaskInstanceId,sameLeveltaskInfo.getTaskInfoId()))
				{
					return false;
				}
			}
		}
		
		if(sameLevelGroupTaskList != null && sameLevelGroupTaskList.size() > 0)
		{
			for(GroupTaskInfo subGrouptask : sameLevelGroupTaskList)
			{
				if(subGrouptask != null && existGroupTaskInstance(groupTaskInstanceId, subGrouptask.getGroupTaskId()))
				{
					return false;
				}
			}
		}
		
		return true;
	}
	
	private boolean existTaskInstance(String groupTaskInstanceId, String taskId)
	{
		Map<String, TaskInstance> map = GroupTaskContext.getALLRunTask(groupTaskInstanceId);
		
		for(Map.Entry<String, TaskInstance> entry : map.entrySet())
		{
			TaskInstance taskInstance = entry.getValue();
			if(taskInstance.getTaskId().equals(taskId))
			{
				return true;
			}
		}
		return false;
	}
	
	private boolean existGroupTaskInstance(String groupTaskInstanceId, String subGroupTaskId)
	{
		Map<String, GroupTaskInstance> map = GroupTaskContext.getAllRunGroupTask();
		
		for(Map.Entry<String, GroupTaskInstance> entry : map.entrySet())
		{
			GroupTaskInstance groupTaskInstance = entry.getValue();
			if(groupTaskInstanceId.equals(groupTaskInstance.getSuperGroupTaskInstanceId()) && groupTaskInstance.getGroupTaskId().equals(subGroupTaskId))
			{
				return true;
			}
		}
		return false;
	}
	
	
	private List<TaskInfo> getSameOrderTask(GroupTaskInfo groupTaskInfo , int order)
	{		
		if(groupTaskInfo != null && groupTaskInfo.getTaskInfoList() != null  && groupTaskInfo.getTaskInfoList().size() > 0)
		{
			 List<TaskInfo> taskList = new ArrayList<TaskInfo>();
			 for(TaskInfo taskInfo : groupTaskInfo.getTaskInfoList())
			 {
				 if(taskInfo.getOrderNo() == order)
				 {
					 taskList.add(taskInfo);
				 }
			 }
			 return taskList;
		}
		return null;
	}
	
	private List<GroupTaskInfo> getSameSubGroupTask(GroupTaskInfo groupTaskInfo , int order)
	{
		if(groupTaskInfo != null && groupTaskInfo.getSubGroupTaskInfoList() != null  && groupTaskInfo.getSubGroupTaskInfoList().size() > 0)
		{
			 List<GroupTaskInfo> grouptaskList = new ArrayList<GroupTaskInfo>();
			 for(GroupTaskInfo subGroupTaskInfo : groupTaskInfo.getSubGroupTaskInfoList())
			 {
				 if(subGroupTaskInfo.getOrderNo() == order)
				 {
					 grouptaskList.add(subGroupTaskInfo);
				 }
			 }
			 return grouptaskList;
		}
		else
		{
			return null;
		}
	}
	
	private List<TaskInfo> getFirstTaskInfo(GroupTaskInfo groupTaskInfo, int orderNo)
	{
		if (groupTaskInfo != null && groupTaskInfo.getTaskInfoList() != null
				&& groupTaskInfo.getTaskInfoList().size() > 0)
		{
			List<TaskInfo> taskInfoList = new ArrayList<TaskInfo>();

			TaskInfo fisrtTaskInfo = null;
			for (TaskInfo taskInfo : groupTaskInfo.getTaskInfoList())
			{
				if(fisrtTaskInfo == null && orderNo < taskInfo.getOrderNo())
				{	
					fisrtTaskInfo = taskInfo;
					taskInfoList.add(fisrtTaskInfo);
				}
				else if(fisrtTaskInfo == null && orderNo >= taskInfo.getOrderNo())
				{
					continue;
				}
				else
				{
					if (taskInfo.getOrderNo() > orderNo && taskInfo.getOrderNo() < fisrtTaskInfo.getOrderNo())
					{
						taskInfoList.clear();
						fisrtTaskInfo = taskInfo;
						taskInfoList.add(taskInfo);
					}
					else if(taskInfo.getOrderNo() > orderNo && taskInfo.getOrderNo() == fisrtTaskInfo.getOrderNo())
					{
						taskInfoList.add(taskInfo);
					}
				}
			}

			return taskInfoList;
		}
		else
		{
			return null;
		}
	}
	
	private  List<GroupTaskInfo> getFirstSubGroupTaskInfo(GroupTaskInfo groupTaskInfo, int orderNo)
	{
		if (groupTaskInfo != null && groupTaskInfo.getSubGroupTaskInfoList() != null
				&& groupTaskInfo.getSubGroupTaskInfoList().size() > 0)
		{
			List<GroupTaskInfo> groupTaskInfoList = new ArrayList<GroupTaskInfo>();

			
			GroupTaskInfo fisrtGroupTaskInfo = null;
			for (GroupTaskInfo subGroupTaskInfo : groupTaskInfo.getSubGroupTaskInfoList())
			{
				
				if(fisrtGroupTaskInfo == null && orderNo < subGroupTaskInfo.getOrderNo())
				{	
					fisrtGroupTaskInfo = subGroupTaskInfo;
					groupTaskInfoList.add(fisrtGroupTaskInfo);
				}
				else if(fisrtGroupTaskInfo == null && orderNo >= subGroupTaskInfo.getOrderNo())
				{
					continue;
				}
				else
				{
					if (subGroupTaskInfo.getOrderNo() > orderNo && subGroupTaskInfo.getOrderNo() < fisrtGroupTaskInfo.getOrderNo())
					{
						groupTaskInfoList.clear();
						fisrtGroupTaskInfo = subGroupTaskInfo;
						groupTaskInfoList.add(fisrtGroupTaskInfo);
					}
					else if(subGroupTaskInfo.getOrderNo() > orderNo && subGroupTaskInfo.getOrderNo() == fisrtGroupTaskInfo.getOrderNo())
					{
						groupTaskInfoList.add(subGroupTaskInfo);
					}
				}
			}

			return groupTaskInfoList;
		}
		else
		{
			return null;
		}
	}
	
	public void resumeTask(TaskInstance taskInstance)
	{
		Map<String, Object> map = paramService.produceParamInstanceMap(taskInstance);
		TaskInfo taskInfo = GroupTaskContext.getTaskInfo(taskInstance.getGroupTaskId(), taskInstance.getTaskId());
		TaskType taskType = taskInfo.getTaskType();
		Task task = taskTypeFactory.getTaskByTaskTaskType(taskType);
		runTask(taskType,task,taskInstance,map);
	}
	
	
	private void runTask(TaskType taskType,Task task, TaskInstance taskInstance,Map<String, Object> paramMap)
	{
		RunTask runTask = new RunTask(task,taskInstance,paramMap,this,taskDao,paramService);
		ThreadPoolExecutor poolExecutor = GroupTaskContext.getThreadPoolExecutor(taskType.getTaskTypeId());
		poolExecutor.prestartCoreThread();
		poolExecutor.getQueue().add(runTask);
		GroupTaskContext.addRunTask(taskInstance);
	}
	
	private GroupTaskInstance createGroupTaskInstance(GroupTaskInfo groupTaskInfo, GroupTaskInstance superGroupTaskInstance)
	{
		String groupTaskInstanceId = serialService.newSerialNo(null);
		GroupTaskInstance groupTaskInstance = new GroupTaskInstance();
		groupTaskInstance.setGroupTaskId(groupTaskInfo.getGroupTaskId());
		groupTaskInstance.setGroupTaskInstanceId(groupTaskInstanceId);
		groupTaskInstance.setRunTime(new Date());
		groupTaskInstance.setStatus(TimingConst.GROUPTASK_STATUS_RUN);
		if(superGroupTaskInstance != null)
		{
			groupTaskInstance.setSuperGroupTaskInstanceId(superGroupTaskInstance.getGroupTaskInstanceId());
		}
		taskDao.insertGroupTaskInstance(groupTaskInstance);
		return groupTaskInstance;
	}
	
	private TaskInstance createTaskInstance(TaskInfo taskInfo, GroupTaskInstance groupTaskInstance)
	{
		TaskInstance taskInstance = new TaskInstance();
		String taskInstanceId = serialService.newSerialNo(null);
		taskInstance.setOperator(TimingConst.SYSTEM_OPERATOR);
		taskInstance.setRunTime(new Date());
		taskInstance.setStatus(TimingConst.TASK_STATUS_RUN);
		taskInstance.setTaskId(taskInfo.getTaskInfoId());
		taskInstance.setTaskInstanceId(taskInstanceId);
		taskInstance.setGroupTaskId(taskInfo.getTaskGroupId());
		taskInstance.setGroupTaskInstanceId(groupTaskInstance.getGroupTaskInstanceId());
		taskDao.insertTaskInstance(taskInstance);
		return taskInstance;
	}

	public SerialService getSerialService()
	{
		return serialService;
	}

	public void setSerialService(SerialService serialService)
	{
		this.serialService = serialService;
	}

	public TaskTypeFactory getTaskTypeFactory()
	{
		return taskTypeFactory;
	}

	public void setTaskTypeFactory(TaskTypeFactory taskTypeFactory)
	{
		this.taskTypeFactory = taskTypeFactory;
	}

	public TaskDao getTaskDao()
	{
		return taskDao;
	}

	public void setTaskDao(TaskDao taskDao)
	{
		this.taskDao = taskDao;
	}

	public ParamService getParamService()
	{
		return paramService;
	}

	public void setParamService(ParamService paramService)
	{
		this.paramService = paramService;
	}
}